<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>WebGL - 3D Textures</title>
    <link type="text/css" href="../resources/webgl-tutorials.css" rel="stylesheet" />
</head>

<body>
    <div id="info">
        <div class="description">
            Textured F with Fs
        </div>
        <a href="https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-3d-textures.html" target="_blank">WebGL
            三维纹理</a>
    </div>
    <canvas id="canvas"></canvas>
</body>
<!-- vertex shader -->
<script id="3d-vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec2 a_texcoord;

uniform mat4 u_matrix;

varying vec2 v_texcoord;

void main() {
    // 将位置和矩阵相乘
    gl_Position = u_matrix * a_position;

    // 传递纹理坐标到片断着色器
    v_texcoord = a_texcoord;
}
</script>
<!-- fragment shader -->
<script id="3d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// 从顶点着色器中传入的值
varying vec2 v_texcoord;

// 纹理
uniform sampler2D u_texture;

void main() {
   gl_FragColor = texture2D(u_texture, v_texcoord);
}
</script>
<!--
for most samples webgl-utils only provides shader compiling/linking and
canvas resizing because why clutter the examples with code that's the same in every sample.
See http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and http://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="../resources/webgl-utils.js"></script>
<script src="../resources/m4.js"></script>
<script>
    "use strict";

    function main() {
        // Get A WebGL context
        /** @type {HTMLCanvasElement} */
        var canvas = document.getElementById("canvas");
        var gl = canvas.getContext("webgl");
        if (!gl) {
            return;
        }

        // setup GLSL program
        var program = webglUtils.createProgramFromScripts(gl, ["3d-vertex-shader", "3d-fragment-shader"]);

        // 找到顶点坐标中的属性
        var positionLocation = gl.getAttribLocation(program, "a_position");
        var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");

        // lookup uniforms
        var matrixLocation = gl.getUniformLocation(program, "u_matrix");
        var textureLocation = gl.getUniformLocation(program, "u_texture");

        // Create a buffer for positions
        var positionBuffer = gl.createBuffer();
        // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        // Put the positions in the buffer
        setGeometry(gl);

        // 为纹理坐标创建一个缓冲
        var texcoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
        // 设置纹理坐标
        setTexcoords(gl);

        // 创建一个纹理
        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 用 1x1 个蓝色像素填充纹理
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
            new Uint8Array([0, 0, 255, 255]));
        // 异步加载图像
        var image = new Image();
        image.src = "../resources/f-texture.png";
        image.addEventListener('load', function () {
            // 现在图像加载完成，拷贝到纹理中
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            gl.generateMipmap(gl.TEXTURE_2D);
        });


        function radToDeg(r) {
            return r * 180 / Math.PI;
        }

        function degToRad(d) {
            return d * Math.PI / 180;
        }

        var fieldOfViewRadians = degToRad(60);
        var modelXRotationRadians = degToRad(0);
        var modelYRotationRadians = degToRad(0);

        var then = 0;

        requestAnimationFrame(drawScene);

        // Draw the scene.
        function drawScene(now) {
            // Convert to seconds
            now *= 0.001;
            // Subtract the previous time from the current time
            var deltaTime = now - then;
            // Remember the current time for the next frame.
            then = now;

            webglUtils.resizeCanvasToDisplaySize(gl.canvas);

            // Tell WebGL how to convert from clip space to pixels
            gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

            gl.enable(gl.CULL_FACE);
            gl.enable(gl.DEPTH_TEST);

            // Animate the rotation
            modelXRotationRadians += 1.2 * deltaTime;
            modelYRotationRadians += 0.7 * deltaTime;

            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            // Tell it to use our program (pair of shaders)
            gl.useProgram(program);

            // Turn on the position attribute
            gl.enableVertexAttribArray(positionLocation);

            // Bind the position buffer.
            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

            // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
            var size = 3;          // 3 components per iteration
            var type = gl.FLOAT;   // the data is 32bit floats
            var normalize = false; // don't normalize the data
            var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
            var offset = 0;        // start at the beginning of the buffer
            gl.vertexAttribPointer(
                positionLocation, size, type, normalize, stride, offset);

            // Turn on the teccord attribute
            gl.enableVertexAttribArray(texcoordLocation);

            // Bind the position buffer.
            gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);

            // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
            var size = 2;          // 2 components per iteration
            var type = gl.FLOAT;   // the data is 32bit floats
            var normalize = false; // don't normalize the data
            var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
            var offset = 0;        // start at the beginning of the buffer
            // 以浮点型格式传递纹理坐标
            gl.vertexAttribPointer(
                texcoordLocation, size, type, normalize, stride, offset);

            // Compute the projection matrix
            var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
            var projectionMatrix =
                m4.perspective(fieldOfViewRadians, aspect, 1, 2000);

            var cameraPosition = [0, 0, 200];
            var up = [0, 1, 0];
            var target = [0, 0, 0];

            // Compute the camera's matrix using look at.
            var cameraMatrix = m4.lookAt(cameraPosition, target, up);

            // Make a view matrix from the camera matrix.
            var viewMatrix = m4.inverse(cameraMatrix);

            var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

            var matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
            matrix = m4.yRotate(matrix, modelYRotationRadians);

            // Set the matrix.
            gl.uniformMatrix4fv(matrixLocation, false, matrix);

            // Tell the shader to use texture unit 0 for u_texture
            gl.uniform1i(textureLocation, 0);

            // Draw the geometry.
            gl.drawArrays(gl.TRIANGLES, 0, 16 * 6);

            requestAnimationFrame(drawScene);
        }
    }

    // Fill the buffer with the values that define a letter 'F'.
    function setGeometry(gl) {
        var positions = new Float32Array([
            // left column front
            0, 0, 0,
            0, 150, 0,
            30, 0, 0,
            0, 150, 0,
            30, 150, 0,
            30, 0, 0,

            // top rung front
            30, 0, 0,
            30, 30, 0,
            100, 0, 0,
            30, 30, 0,
            100, 30, 0,
            100, 0, 0,

            // middle rung front
            30, 60, 0,
            30, 90, 0,
            67, 60, 0,
            30, 90, 0,
            67, 90, 0,
            67, 60, 0,

            // left column back
            0, 0, 30,
            30, 0, 30,
            0, 150, 30,
            0, 150, 30,
            30, 0, 30,
            30, 150, 30,

            // top rung back
            30, 0, 30,
            100, 0, 30,
            30, 30, 30,
            30, 30, 30,
            100, 0, 30,
            100, 30, 30,

            // middle rung back
            30, 60, 30,
            67, 60, 30,
            30, 90, 30,
            30, 90, 30,
            67, 60, 30,
            67, 90, 30,

            // top
            0, 0, 0,
            100, 0, 0,
            100, 0, 30,
            0, 0, 0,
            100, 0, 30,
            0, 0, 30,

            // top rung right
            100, 0, 0,
            100, 30, 0,
            100, 30, 30,
            100, 0, 0,
            100, 30, 30,
            100, 0, 30,

            // under top rung
            30, 30, 0,
            30, 30, 30,
            100, 30, 30,
            30, 30, 0,
            100, 30, 30,
            100, 30, 0,

            // between top rung and middle
            30, 30, 0,
            30, 60, 30,
            30, 30, 30,
            30, 30, 0,
            30, 60, 0,
            30, 60, 30,

            // top of middle rung
            30, 60, 0,
            67, 60, 30,
            30, 60, 30,
            30, 60, 0,
            67, 60, 0,
            67, 60, 30,

            // right of middle rung
            67, 60, 0,
            67, 90, 30,
            67, 60, 30,
            67, 60, 0,
            67, 90, 0,
            67, 90, 30,

            // bottom of middle rung.
            30, 90, 0,
            30, 90, 30,
            67, 90, 30,
            30, 90, 0,
            67, 90, 30,
            67, 90, 0,

            // right of bottom
            30, 90, 0,
            30, 150, 30,
            30, 90, 30,
            30, 90, 0,
            30, 150, 0,
            30, 150, 30,

            // bottom
            0, 150, 0,
            0, 150, 30,
            30, 150, 30,
            0, 150, 0,
            30, 150, 30,
            30, 150, 0,

            // left side
            0, 0, 0,
            0, 0, 30,
            0, 150, 30,
            0, 0, 0,
            0, 150, 30,
            0, 150, 0]);

        // Center the F around the origin and Flip it around. We do this because
        // we're in 3D now with and +Y is up where as before when we started with 2D
        // we had +Y as down.

        // We could do by changing all the values above but I'm lazy.
        // We could also do it with a matrix at draw time but you should
        // never do stuff at draw time if you can do it at init time.
        var matrix = m4.identity();// m4.xRotation(Math.PI);
        matrix = m4.translate(matrix, -50, -75, -15);

        for (var ii = 0; ii < positions.length; ii += 3) {
            var vector = m4.transformVector(matrix, [positions[ii + 0], positions[ii + 1], positions[ii + 2], 1]);
            positions[ii + 0] = vector[0];
            positions[ii + 1] = vector[1];
            positions[ii + 2] = vector[2];
        }

        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
    }

    // 为 F 设置纹理坐标缓冲
    function setTexcoords(gl) {
        gl.bufferData(
            gl.ARRAY_BUFFER,
            new Float32Array([
                // left column front
                0, 0,
                0, 1,
                1, 0,
                0, 1,
                1, 1,
                1, 0,

                // top rung front
                0, 0,
                0, 1,
                1, 0,
                0, 1,
                1, 1,
                1, 0,

                // middle rung front
                0, 0,
                0, 1,
                1, 0,
                0, 1,
                1, 1,
                1, 0,

                // left column back
                0, 0,
                1, 0,
                0, 1,
                0, 1,
                1, 0,
                1, 1,

                // top rung back
                0, 0,
                1, 0,
                0, 1,
                0, 1,
                1, 0,
                1, 1,

                // middle rung back
                0, 0,
                1, 0,
                0, 1,
                0, 1,
                1, 0,
                1, 1,

                // top
                0, 0,
                1, 0,
                1, 1,
                0, 0,
                1, 1,
                0, 1,

                // top rung right
                0, 0,
                1, 0,
                1, 1,
                0, 0,
                1, 1,
                0, 1,

                // under top rung
                0, 0,
                0, 1,
                1, 1,
                0, 0,
                1, 1,
                1, 0,

                // between top rung and middle
                0, 0,
                1, 1,
                0, 1,
                0, 0,
                1, 0,
                1, 1,

                // top of middle rung
                0, 0,
                1, 1,
                0, 1,
                0, 0,
                1, 0,
                1, 1,

                // right of middle rung
                0, 0,
                1, 1,
                0, 1,
                0, 0,
                1, 0,
                1, 1,

                // bottom of middle rung.
                0, 0,
                0, 1,
                1, 1,
                0, 0,
                1, 1,
                1, 0,

                // right of bottom
                0, 0,
                1, 1,
                0, 1,
                0, 0,
                1, 0,
                1, 1,

                // bottom
                0, 0,
                0, 1,
                1, 1,
                0, 0,
                1, 1,
                1, 0,

                // left side
                0, 0,
                0, 1,
                1, 1,
                0, 0,
                1, 1,
                1, 0]),
            gl.STATIC_DRAW);
    }

    main();
</script>

</html>